package cn.edu.csust.coursecard.aspect;

import cn.edu.csust.coursecard.annotation.ClusterTask;
import cn.edu.csust.coursecard.common.CodeEnum;
import cn.edu.csust.coursecard.dao.dto.TaskRequest;
import cn.edu.csust.coursecard.dao.dto.TaskResult;
import cn.edu.csust.coursecard.exception.BaseException;
import cn.edu.csust.coursecard.service.IClusterTaskService;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Component
@Aspect
public class ClusterTaskAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(ClusterTaskAspect.class);
    
    @Resource
    private IClusterTaskService clusterTaskService;

    @Pointcut("@annotation(cn.edu.csust.coursecard.annotation.ClusterTask)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        if (pjp == null) {
            return null;
        }

        Signature signature = pjp.getSignature();
        MethodSignature sig = (MethodSignature) signature;
        Method method = sig.getMethod();
        ClusterTask annotation = method.getAnnotation(ClusterTask.class);

        String taskName;
        if (annotation.value().isEmpty()) {
            taskName = sig.getName();
        } else {
            taskName = annotation.value();
        }

        Class<?> returnType = sig.getMethod().getReturnType();
        if (returnType != String.class) {
            LOGGER.error("[ClusterTask]注解不支持的方法返回值：{} (应为：{})", returnType, String.class);
            return pjp.proceed();
        }

        // 无节点连接，则转到主机执行
        int connCount = clusterTaskService.getConnCount();
        if (connCount < 1) {
            return pjp.proceed();
        }

        // 有节点连接
        Object[] argValues = pjp.getArgs();
        String[] argNames = sig.getParameterNames();

        TaskRequest req = new TaskRequest("task", "", taskName, null);
        Map<String, String> payloadMap = new HashMap<>();
        for (int i = 0; i < argNames.length; i++) {
            payloadMap.put(argNames[i], argValues[i].toString());
        }
        req.setPayload(payloadMap);
        String taskId = clusterTaskService.newTask(req);

        // 目前采用轮询检查是否返回值，后期可能改事件订阅
        // timeout: 200ms*10=2000ms
        try {
            for (int i = 0; i < 10; i++) {
                TaskResult result = clusterTaskService.getResult(taskId);
                if (result != null) {
                    if (StringUtils.isBlank(result.getError())) {
                        return result.getPayload();
                    } else {
                        throw new BaseException(CodeEnum.SYSTEM_ERROR, "远程任务处理错误: " + result.getError());
                    }
                }
                Thread.sleep(200);
            }
        } catch (JsonProcessingException e) {
            // 远程处理出错转到主机执行
            LOGGER.error("task: {}, taskName: {}, err: {}, 转到主机执行。", req.getTaskId(), req.getTaskName(), e.getMessage());
            return pjp.proceed();
        }

        // 超时后仍转到主机执行
        LOGGER.error("task: {}, taskName: {}, err: {}, 转到主机执行。", req.getTaskId(), req.getTaskName(), "远程处理超时");
        return pjp.proceed();
    }

}
